#include "ase.h"

#include <fstream>
#include <cstring>

#ifdef MFC_ENABLED
#include "DecoDirectoryManager.h"
#endif
using namespace std;

const int BUFFERSIZE = 256;
const int MYBUFFER   = 256;

static const char
sz3DSMAX_ASCIIEXPORT[]          =       "*3DSMAX_ASCIIEXPORT",
szBITMAP[]                      =       "*BITMAP",
szBITMAP_FILTER[]				=		"*BITMAP_FILTER",
szCOMMENT[]                     =       "*COMMENT",
szGEOMOBJECT[]                  =       "*GEOMOBJECT",
szINHERIT_POS[]                 =       "*INHERIT_POS",
szINHERIT_ROT[]                 =       "*INHERIT_ROT",
szINHERIT_SCL[]                 =       "*INHERIT_SCL",
szMAP_DIFFUSE[]                 =       "*MAP_DIFFUSE",
szMAP_AMBIENT[]					=		"*MAP_AMBIENT",
szMAP_OPACITY[]				    =		"*MAP_OPACITY",
szMAP_SPECULAR[]				=		"*MAP_SPECULAR",
szMAP_REFLECT[]					=		"*MAP_REFLECT",
szMAP_BUMP[]					=		"*MAP_BUMP",
szMAP_NAME[]                    =       "*MAP_NAME",
szMAP_CLASS[]					=		"*MAP_CLASS",
szMAP_SUBNO[]					=		"*MAP_SUBNO",
szMAP_AMOUNT[]                  =       "*MAP_AMOUNT",
szMAP_TYPE[]					=		"*MAP_TYPE",
szMATERIAL[]                    =       "*MATERIAL",
szMATERIAL_AMBIENT[]            =       "*MATERIAL_AMBIENT",
szMATERIAL_CLASS[]              =       "*MATERIAL_CLASS",
szMATERIAL_COUNT[]              =       "*MATERIAL_COUNT",
szMATERIAL_DIFFUSE[]            =       "*MATERIAL_DIFFUSE",
szMATERIAL_LIST[]               =       "*MATERIAL_LIST",
szMATERIAL_NAME[]               =       "*MATERIAL_NAME",
szMATERIAL_REF[]                =       "*MATERIAL_REF",
szMATERIAL_SPECULAR[]           =       "*MATERIAL_SPECULAR",
szMATERIAL_SHINE[]              =       "*MATERIAL_SHINE",
szMATERIAL_SHINESTRENGTH[]      =       "*MATERIAL_SHINESTRENGTH",
szMATERIAL_SHADING[]            =       "*MATERIAL_SHADING",
szMATERIAL_TRANSPARENCY[]       =       "*MATERIAL_TRANSPARENCY",
szMATERIAL_WIRESIZE[]           =       "*MATERIAL_WIRESIZE",
szMATERIAL_TWOSIDED[]			=		"*MATERIAL_TWOSIDED",
szMESH[]                        =       "*MESH",
szMESH_FACE[]                   =       "*MESH_FACE",
szMESH_FACENORMAL[]             =       "*MESH_FACENORMAL",
szMESH_FACE_LIST[]              =       "*MESH_FACE_LIST",
szMESH_MTLID[]                  =       "*MESH_MTLID",
szMESH_NUMVERTEX[]              =       "*MESH_NUMVERTEX",
szMESH_NUMFACES[]               =       "*MESH_NUMFACES",
szMESH_NUMTVERTEX[]             =       "*MESH_NUMTVERTEX",
szMESH_NUMCVERTEX[]             =       "*MESH_NUMCVERTEX",
szMESH_NORMALS[]                =       "*MESH_NORMALS",
szMESH_NUMTVFACES[]             =       "*MESH_NUMTVFACES",
szMESH_TFACELIST[]              =       "*MESH_TFACELIST",
szMESH_TFACE[]                  =       "*MESH_TFACE",
szMESH_TVERTLIST[]              =       "*MESH_TVERTLIST",
szMESH_TVERT[]                  =       "*MESH_TVERT",
szMESH_VERTEXNORMAL[]           =       "*MESH_VERTEXNORMAL",
szMESH_VERTEX_LIST[]            =       "*MESH_VERTEX_LIST",
szMESH_VERTEX[]                 =       "*MESH_VERTEX",
szNODE_NAME[]                   =       "*NODE_NAME",
szNODE_TM[]                     =       "*NODE_TM",
szNUMSUBMTLS[]					=		"*NUMSUBMTLS",
szPROP_MOTIONBLUR[]             =       "*PROP_MOTIONBLUR",
szPROP_CASTSHADOW[]             =       "*PROP_CASTSHADOW",
szPROP_RECVSHADOW[]             =       "*PROP_RECVSHADOW",
szSCENE[]                       =       "*SCENE",
szSCENE_FILENAME[]              =       "*SCENE_FILENAME",
szSCENE_BACKGROUND_STATIC[]     =       "*SCENE_BACKGROUND_STATIC",
szSCENE_AMBIENT_STATIC[]        =       "*SCENE_AMBIENT_STATIC",
szSUBMATERIAL[]					=		"*SUBMATERIAL",
szTIMEVALUE[]                   =       "*TIMEVALUE",
szTM_ROW0[]                     =       "*TM_ROW0",
szTM_ROW1[]                     =       "*TM_ROW1",
szTM_ROW2[]                     =       "*TM_ROW2",
szTM_ROW3[]                     =       "*TM_ROW3",
szTM_POS[]                      =       "*TM_POS",
szTM_ROTAXIS[]                  =       "*TM_ROTAXIS",
szTM_ROTANGLE[]                 =       "*TM_ROTANGLE",
szTM_SCALE[]                    =       "*TM_SCALE",
szTM_SCALEAXIS[]                =       "*TM_SCALEAXIS",
szTM_SCALEAXISANG[]             =       "*TM_SCALEAXISANG",
szUVW_ANGLE[]                   =       "*UVW_ANGLE",
szUVW_BLUR[]                    =       "*UVW_BLUR",
szUVW_BLUR_OFFSET[]             =       "*UVW_BLUR_OFFSET",
szUVW_NOISE_AMT[]				=		"*UVW_NOISE_AMT",
szUVW_NOISE_SIZE[]				=		"*UVW_NOISE_SIZE",
szUVW_NOISE_LEVEL[]				=		"*UVW_NOISE_LEVEL",
szUVW_NOISE_PHASE[]				=		"*UVW_NOISE_PHASE",
szUVW_U_OFFSET[]                =       "*UVW_U_OFFSET",
szUVW_V_OFFSET[]                =       "*UVW_V_OFFSET",
szUVW_U_TILING[]                =       "*UVW_U_TILING",
szUVW_V_TILING[]                =       "*UVW_V_TILING";

// auxilury functions
bool FindInFile(ifstream& file, const char* const szTarget)
{
	char szBuffer[BUFFERSIZE];
	memset(szBuffer, 'x', sizeof(char) * BUFFERSIZE);
	const streampos filePos = file.tellg();

	file.clear();
	while (file.eof() == 0)
	{
		file >> szBuffer;
		if (!strcmp(szBuffer, szTarget))
			return true;
	}
	file.clear();
	file.seekg(filePos);
	return false;
}

bool FindInFileBefore(ifstream& file, const char* const szTarget, const char* const szStop)
{
	char szBuffer[BUFFERSIZE];
	memset(szBuffer, 'x', sizeof(char) * BUFFERSIZE);
	const streampos filePos = file.tellg();

	file.clear();
	while (file.eof() == 0)
	{
		file >> szBuffer;
		if (!strcmp(szBuffer, szStop))
		{
			file.seekg(filePos);
			return false;
		}
		if (!strcmp(szBuffer, szTarget))
			return true;
	}
	file.clear();
	file.seekg(filePos);
	return false;
}

/**
*   Reads in the node header of a Geomobject. The first value read in is *NODE_NAME.
*/ 
bool ASE::GeomObjectNode::read(std::ifstream& ifile)
{
	char buf[MYBUFFER];
	iErrorNo = 0;
	try{
		if( false == FindInFileBefore(ifile, szNODE_NAME, szGEOMOBJECT) )throw(int(-1));
		ifile.getline(buf, MYBUFFER, '"');
		ifile.getline(buf, MYBUFFER, '"');
		node_name = buf;

		if( false == FindInFileBefore(ifile, szINHERIT_POS, szGEOMOBJECT) )throw(int(-2));
		ifile >> inherit_pos[0] >> inherit_pos[1] >> inherit_pos[2];

		if( false == FindInFileBefore(ifile, szINHERIT_ROT, szGEOMOBJECT) )throw(int(-3));
		ifile >> inherit_rot[0] >> inherit_rot[1] >> inherit_rot[2];

		if( false == FindInFileBefore(ifile, szINHERIT_SCL, szGEOMOBJECT) )throw(int(-4));
		ifile >> inherit_scl[0] >> inherit_scl[1] >> inherit_scl[2];


		if( false == FindInFileBefore(ifile, szTM_ROW0, szGEOMOBJECT) )throw(int(-5));
		ifile >> TMRow0[0] >> TMRow0[1] >> TMRow0[2];

		if( false == FindInFileBefore(ifile, szTM_ROW1, szGEOMOBJECT) )throw(int(-6));
		ifile >> TMRow1[0] >> TMRow1[1] >> TMRow1[2];

		if( false == FindInFileBefore(ifile, szTM_ROW2, szGEOMOBJECT) )throw(int(-7));
		ifile >> TMRow2[0] >> TMRow2[1] >> TMRow2[2];


		if( false == FindInFileBefore(ifile, szTM_POS, szGEOMOBJECT) )throw(int(-8));
		ifile >> TMPos[0] >> TMPos[1] >> TMPos[2];

		if( false == FindInFileBefore(ifile, szTM_ROTAXIS, szGEOMOBJECT) )throw(int(-9));
		ifile >> TMRotAxis[0] >> TMRotAxis[1] >> TMRotAxis[2];

		if( false == FindInFileBefore(ifile, szTM_ROTANGLE, szGEOMOBJECT) )throw(int(-10));
		ifile >> TMRotAngle;


		if( false == FindInFileBefore(ifile, szTM_SCALE, szGEOMOBJECT) )throw(int(-11));
		ifile >> TMScale[0] >> TMScale[1] >> TMScale[2];

		if( false == FindInFileBefore(ifile, szTM_SCALEAXIS, szGEOMOBJECT) )throw(int(-12));
		ifile >> TMScaleAxis[0] >> TMScaleAxis[1] >> TMScaleAxis[2];

		if( false == FindInFileBefore(ifile, szTM_SCALEAXISANG, szGEOMOBJECT) )throw(int(-13));
		ifile >> TMScaleAxisAng;
	}
	catch(int i){
		iErrorNo = i;
		return false;
	}
	catch(...){
		iErrorNo = UNKNOWNERROR;
		return false;
	}
	return true;
}

/**
*   Reads in a single mesh values. The first value read in is *TIMEVALUE. 
*/
bool ASE::GeomObjectMesh::read(std::ifstream& ifile)
{
	char buf[MYBUFFER];

	iErrorNo = 0;
	ulFlags = 0;

	vertex_list.clear();
	face_list.clear();
	face_edge_list.clear();
	face_list_materialID.clear();
	tvert_list.clear();
	tface_list.clear();
	face_normals.clear();
	vertex_normals.clear();

	try{
		if (FindInFileBefore(ifile, szTIMEVALUE, szGEOMOBJECT) )
		{
			ifile >> time_value;
			ulFlags |= MESHTIMEVALUE;
		}


		if (false == FindInFileBefore(ifile, szMESH_NUMVERTEX, szGEOMOBJECT)) throw (int(-2));
		ifile >> vertex_count;
		assert( vertex_count || !"Error : Zero Vertices." );
		if ( 0 == vertex_count ) throw( NOVERTICES );


		if (false == FindInFileBefore(ifile, szMESH_NUMFACES, szGEOMOBJECT)) throw (int(-3));
		ifile >> face_count;
		assert( face_count || !"Error : Zero Faces." );
		if( 0 == face_count ) throw( NOFACES );

		// Dummy is a check variable. It should always equal to the number of the
		// data being read.
		int iDummy(0);
		GeomPoint p;

		// Find start of vertex list.
		if (false == FindInFileBefore(ifile, szMESH_VERTEX_LIST, szGEOMOBJECT)) throw (int(-4));

		// Reserve known spaces in vectors.
		vertex_list.reserve(vertex_count);

		// Read in the vertex list.
		for (int i = 0; i < vertex_count; ++i)
		{
			if (false == FindInFileBefore(ifile, szMESH_VERTEX, szGEOMOBJECT) )throw(int(-5 -i));

			ifile >> iDummy;
			assert( iDummy == i || !"Error : Mismatching dummy and mesh vertex number." );
			if( iDummy != i ) throw (int(-5 -i));

			ifile >> p.x >> p.y >> p.z;

			vertex_list.push_back( p );
		}

		// Reserve space
		face_list.reserve( 3 * face_count );
		face_edge_list.reserve( 3 * face_count );
		face_list_materialID.reserve( face_count );

		// Find start of face list.
		if (false == FindInFileBefore(ifile, szMESH_FACE_LIST, szGEOMOBJECT)) throw (int(-6));

		// Read in the vertex list.
		char cdummy;
		int ia, ib, ic;
		int iab, ibc, ica;
		int imid;
		for (int i = 0; i < face_count; ++i)
		{
			if (false == FindInFileBefore(ifile, szMESH_FACE, szGEOMOBJECT)) throw (int(-6));

			ifile >> iDummy;
			assert( iDummy == i || !"Error : Mismatching dummy and face vertex number." );
			if( iDummy != i )throw (int(-5));

			// A line here looks like this:
			// *MESH_FACE    0:    A:    0 B:    1 C:    9 AB:    1 BC:    1 CA:    0	 *MESH_SMOOTHING 1,9,12 	*MESH_MTLID 2

			ifile >> cdummy; // the semi colon following dummy.
			ifile >> buf;    // The 'A:'.
			ifile >> ia;     // The integer face number.

			ifile >> buf;   // The 'B:'
			ifile >> ib;    // The integer face number.

			ifile >> buf;   // The 'C:'
			ifile >> ic;    // The integer face number.

			face_list.push_back(ia);
			face_list.push_back(ib);
			face_list.push_back(ic);

			ifile >> buf;   // The 'AB:'.
			ifile >> iab;   // The integer face number.

			ifile >> buf;   // The 'BC:'
			ifile >> ibc;   // The integer face number.

			ifile >> buf;   // The 'CA:'
			ifile >> ica;   // The integer face number.

			face_edge_list.push_back(iab);
			face_edge_list.push_back(ibc);
			face_edge_list.push_back(ica);

			// Try and find the mesh material id number. I am not sure this is always
			// exported so we will not need this nor return an error if it is not there.
			if(FindInFileBefore(ifile, szMESH_MTLID, szMESH_FACE) )
			{
				ifile >> imid;
				face_list_materialID.push_back(imid);
			}
		}

		// Find start of texture vertex list.
		int iTextureVCount = 0;
		if( FindInFileBefore(ifile, szMESH_NUMTVERTEX, szGEOMOBJECT) )
		{
			ifile >> iTextureVCount;
		}

		tface_list.reserve(3 * face_count);
		face_normals.reserve(face_count);
		vertex_normals.reserve(3 * face_count);

		// If the object has no texture list quit reading.
		if( iTextureVCount > 0 )
		{
			ulFlags |= MESHTVERTEX;

			if (false == FindInFileBefore(ifile, szMESH_TVERTLIST, szGEOMOBJECT)) throw ( NOTEXTUREVERTICES );

			tvert_list.reserve( iTextureVCount );

			// Read in the vertex list.
			for(int i=0; i<iTextureVCount; ++i )
			{
				if (false == FindInFileBefore(ifile, szMESH_TVERT, szGEOMOBJECT)) throw (int(-6));

				ifile >> iDummy;
				assert( iDummy == i || !"Error: mismatched dummy and texture vertex." );
				if( iDummy != i ) throw (int(-6));

				ifile >> p.x >> p.y >> p.z;

				tvert_list.push_back( p );
			}

			// Find start of texture face list.
			if (false == FindInFileBefore(ifile, szMESH_TFACELIST, szGEOMOBJECT)) throw ( NOTEXTUREFACES );

			// Read in texture faces.
			for (int i = 0; i < face_count; ++i )
			{
				if (false == FindInFileBefore(ifile, szMESH_TFACE, szGEOMOBJECT)) throw (int(-6));

				ifile >> iDummy;
				assert( i == iDummy || !"Error: mismatched dummy and texture face number." );
				if( i != iDummy )throw(int(-6));

				ifile >> ia >> ib >> ic;

				tface_list.push_back( ia );
				tface_list.push_back( ib );
				tface_list.push_back( ic );
			}
		}
		else
		{
			tvert_list.resize(0);
			tface_list.resize(0);
		}

		// TODO: HANDLE COLOR VERTEX HERE
		// Find start of mesh normals, if there is any.
		if( FindInFileBefore(ifile, szMESH_NORMALS, szGEOMOBJECT) )
		{
			ulFlags |= MESHNORMAL;

			for (int i = 0; i < face_count; ++i )
			{
				if (false == FindInFileBefore(ifile, szMESH_FACENORMAL, szGEOMOBJECT)) throw ( INVALIDMESHNORMALS );

				ifile >> iDummy;
				assert( i == iDummy || !"Error: mismatched dummy and face normal.");
				if( i != iDummy )throw( INVALIDMESHNORMALS );
				ifile >> p.x >> p.y >> p.z;
				face_normals.push_back( p );

				// The three normals at the vertexes on the face.
				if( false == FindInFileBefore(ifile, szMESH_VERTEXNORMAL, szMESH_FACENORMAL) )throw( INVALIDMESHVNORMAL );
				ifile >> iDummy;
				ifile >> p.x >> p.y >> p.z;
				vertex_normals.push_back( p );

				if( false == FindInFileBefore(ifile, szMESH_VERTEXNORMAL, szMESH_FACENORMAL) )throw( INVALIDMESHVNORMAL );
				ifile >> iDummy;
				ifile >> p.x >> p.y >> p.z;
				vertex_normals.push_back( p );

				if( false == FindInFileBefore(ifile, szMESH_VERTEXNORMAL, szMESH_FACENORMAL) )throw( INVALIDMESHVNORMAL );
				ifile >> iDummy;
				ifile >> p.x >> p.y >> p.z;
				vertex_normals.push_back( p );
			}
		}

		// We now have all the arrays data. Now find the little portion 
		// of data at the end of the mesh.
		if( false == FindInFileBefore(ifile, szPROP_MOTIONBLUR, szGEOMOBJECT) )throw( MESHERROR );
		ifile >> ia;
		is_motion_blur = (ia != 0);

		if( false == FindInFileBefore(ifile, szPROP_CASTSHADOW, szGEOMOBJECT) )throw( MESHERROR );      
		ifile >> ia;
		is_cast_shadow = (ia != 0);

		if( false == FindInFileBefore(ifile, szPROP_RECVSHADOW, szGEOMOBJECT) )throw( MESHERROR );
		ifile >> ia;
		is_recv_shadow = (ia != 0);

		if( FindInFileBefore(ifile, szMATERIAL_REF, szGEOMOBJECT) )
		{
			ifile >> material_ref;
		}
		else
		{
			material_ref = -1;
		}
	}
	catch(ERRORCODE e){
		iErrorNo = e;
		return false;
	}    
	catch(int i){
		iErrorNo = i;
		return false;
	}

	catch(...){
		iErrorNo = UNKNOWNERROR;
		return false;
	}
	return true;
}



/**
*   Reads in first the objects node then the mesh. Does not check for
*   the *GEOMOBJECT starting tag.
*/
bool ASE::GeomObject::read(std::ifstream& ifile)
{
	iErrorNo = 0;
	if( false == node.read( ifile ) )
	{
		iErrorNo = node.GetError();
		return false;
	}

	if( false == mesh.read( ifile ) )
	{
		iErrorNo = mesh.GetError();
		return false;
	}

	return true;
}

void ASE::Material::clear()
{
	for (std::vector<Material*>::iterator i = sub_materials.begin();i != sub_materials.end(); ++i)
		delete *i;
	sub_materials.clear();
}

void ASE::Material::copy_from(const Material& material)
{
	material_name = material.material_name;
	material_class = material.material_class;

	ambient[0] = material.ambient[0];
	ambient[1] = material.ambient[1];
	ambient[2] = material.ambient[2];

	diffuse[0] = material.diffuse[0];
	diffuse[1] = material.diffuse[1];
	diffuse[2] = material.diffuse[2];

	specular[0] = material.specular[0];
	specular[1] = material.specular[1];
	specular[2] = material.specular[2];

	shine = material.shine;
	shine_strength = material.shine_strength;
	transparency = material.transparency;
	wire_size = material.wire_size;
	shading = material.shading;
	xp_fallof = material.xp_fallof;
	selfillum = material.selfillum;
	fallof = material.fallof;
	xp_type = material.xp_type;

	map_diffuse = material.map_diffuse;
	map_reflect = material.map_reflect;
	map_ambient = material.map_ambient;
	map_opacity = material.map_opacity;
	map_specular = material.map_specular;
	map_bump = material.map_bump;

	for (std::vector<Material*>::const_iterator i = material.sub_materials.begin();
		i != material.sub_materials.end(); ++i)
	{
		Material* pMat = new Material(**i);
		sub_materials.push_back(pMat);
	}
	num_of_sub_materials = material.num_of_sub_materials;
	ulFlags = material.ulFlags;
}

/**
*   Reads in the material. The first value read in is *MATERIAL_NAME.
*   Does not check for *MATERIAL.
*/
bool ASE::Material::read(std::ifstream& ifile, const char* szSTOP)
{
	char buf[MYBUFFER];

	ulFlags = 0L;
	iErrorNo = 0;

	try{
		streampos pos = ifile.tellg();

		if (FindInFileBefore(ifile, szMATERIAL_NAME, szSTOP))
		{
			ifile.getline(buf, MYBUFFER, '"');
			ifile.getline(buf, MYBUFFER, '"');
			material_name = buf;
			//ifile.seekg( pos );
			ulFlags |= MNAME;
		}

		if (FindInFileBefore(ifile, szMATERIAL_CLASS, szSTOP))
		{
			ifile.getline(buf, MYBUFFER, '"');
			ifile.getline(buf, MYBUFFER, '"');
			material_class = buf;
			//ifile.seekg(pos);
			ulFlags |= MCLASS;
		}

		if (FindInFileBefore(ifile, szMATERIAL_AMBIENT, szSTOP))
		{
			ifile >> ambient[0] >> ambient[1] >> ambient[2];
			//ifile.seekg( pos );
			ulFlags |= MAMBIENT;
		}

		if (FindInFileBefore(ifile, szMATERIAL_DIFFUSE, szSTOP))
		{
			ifile >> diffuse[0] >> diffuse[1] >> diffuse[2];
			//ifile.seekg( pos );
			ulFlags |= MDIFFUSE;
		}

		if (FindInFileBefore(ifile, szMATERIAL_SPECULAR, szSTOP))
		{
			ifile >> specular[0] >> specular[1] >> specular[2];
			//ifile.seekg( pos );
			ulFlags |= MSPECULAR;
		}

		if (FindInFileBefore(ifile, szMATERIAL_SHINE, szSTOP))
		{
			ifile >> shine;
			//ifile.seekg(pos);
			ulFlags |= MSHINE;
		}

		if (FindInFileBefore(ifile, szMATERIAL_SHINESTRENGTH, szSTOP))
		{
			ifile >> shine_strength;
			//ifile.seekg( pos );
			ulFlags |= MSHINESTRENGTH;
		}

		if (FindInFileBefore(ifile, szMATERIAL_TRANSPARENCY, szSTOP))
		{
			ifile >> transparency;
			//ifile.seekg( pos );
			ulFlags |= MTRANSPARENCY;
		}

		if (FindInFileBefore(ifile, szMATERIAL_TWOSIDED, szSTOP))
		{
			ulFlags |= MTWOSIDED;
		}

		if (FindInFileBefore(ifile, szMATERIAL_WIRESIZE, szSTOP))
		{
			ifile >> wire_size;
			//ifile.seekg( pos );
			ulFlags |= MWIRESIZE;
		}

		// TODO: handle sub_material here

		if (FindInFileBefore(ifile, szNUMSUBMTLS, szSTOP))
		{
			ifile >> num_of_sub_materials;

			int iDummy = 0;
			for (int i = 0; i < num_of_sub_materials; ++i)
			{
				if( false == FindInFile(ifile, szSUBMATERIAL) )throw( INVALIDASEFILE );
				ifile >> iDummy;
				assert( i == iDummy || !"Error mismatched sub material and dummy." );
				if( i != iDummy )throw( INVALIDASEFILE );

				ASE::Material *pMat = new ASE::Material();
				pMat->path = path;
				if (i != num_of_sub_materials - 1)
					pMat->read(ifile, szSUBMATERIAL);
				else
					pMat->read(ifile, szSTOP);
				sub_materials.push_back(pMat);
			}
			assert(num_of_sub_materials == sub_materials.size());
			ulFlags |= MSUBMATERIAL;
			pos = ifile.tellg();
		}	

		if (FindInFileBefore(ifile, szMATERIAL_SHADING, szSTOP) )
		{
			ifile >> shading;
			//ifile.seekg( pos );
			ulFlags |= MSHADING;
		}

		if (FindInFileBefore(ifile, szMAP_AMBIENT, szSTOP))
		{
			map_ambient.read(ifile, szSTOP);
			map_ambient.path = path;
			ulFlags |= MMAPAMBIENT;
		}

		if (FindInFileBefore(ifile, szMAP_DIFFUSE, szSTOP))
		{
			map_diffuse.read(ifile, szSTOP);
			map_diffuse.path = path;
			ulFlags |= MMAPDIFFUSE;				
		}

		if (FindInFileBefore(ifile, szMAP_BUMP, szSTOP))
		{
			map_bump.read(ifile, szSTOP);
			map_bump.path = path;
			ulFlags |= MMAPBUMP;
		}

		if (FindInFileBefore(ifile, szMAP_OPACITY, szSTOP))
		{
			map_opacity.read(ifile, szSTOP);
			map_opacity.path = path;
			ulFlags |= MMAPOPACITY;
		}

		if (FindInFileBefore(ifile, szMAP_REFLECT, szSTOP))
		{
			map_reflect.read(ifile, szSTOP);
			map_reflect.path = path;
			ulFlags |= MMAPREFLECT;
		}

		if (FindInFileBefore(ifile, szMAP_SPECULAR, szSTOP))
		{
			map_specular.read(ifile, szSTOP);
			map_specular.path = path;
			ulFlags |= MMAPSPECULAR;
		}
	}
	catch(ERRORCODE e){
		iErrorNo = e;
		return false;
	}
	catch(int i){
		iErrorNo = i;
		return false;
	}
	catch(...){
		iErrorNo = UNKNOWNERROR;
		return false;
	}
	return true;
}


// read map section, the first token to read is "*MAP_NAME"
bool ASE::Map::read(std::ifstream& ifile, const char* szSTOP)
{
	char buf[MYBUFFER];

	ulFlags = 0;
	iErrorNo = 0;

	try{
		const streampos pos = ifile.tellg();

		if ( FindInFileBefore(ifile, szMAP_NAME, szSTOP) )
		{
			ifile.getline(buf, MYBUFFER, '"');
			ifile.getline(buf, MYBUFFER, '"');
			map_name = buf;
			//ifile.seekg( pos );
			ulFlags |= MAPNAME;
		}

		if ( FindInFileBefore(ifile, szMAP_CLASS, szSTOP) )
		{
			ifile.getline(buf, MYBUFFER, '"');
			ifile.getline(buf, MYBUFFER, '"');
			map_class = buf;
			//ifile.seekg( pos );
			ulFlags |= MAPCLASS;
		}

		if (FindInFileBefore(ifile, szMAP_SUBNO, szSTOP))
		{
			ifile >> map_subno;
			//ifile.seekg(pos);
			ulFlags |= MAPSUBNO;
		}

		if (FindInFileBefore(ifile, szMAP_AMOUNT, szSTOP))
		{
			ifile >> map_amount;
			//ifile.seekg(pos);
			ulFlags |= MAPAMOUNT;
		}

		if (FindInFileBefore(ifile, szBITMAP, szSTOP))
		{
			ifile.getline(buf, MYBUFFER, '"');
			ifile.getline(buf, MYBUFFER, '"');
			bitmap = buf;
			//ifile.seekg(pos);
			ulFlags |= MAPBITMAP;
		}

		if (FindInFileBefore(ifile, szMAP_TYPE, szSTOP))
		{
			ifile >> map_type;
			//ifile.seekg(pos);
			ulFlags |= MAPTYPE;
		}

		if (FindInFileBefore(ifile, szUVW_U_OFFSET, szSTOP))
		{
			ifile >> uvw_u_offset;
			//ifile.seekg(pos);
			ulFlags |= MAPUOFFSET;
		}

		if (FindInFileBefore(ifile, szUVW_V_OFFSET, szSTOP))
		{
			ifile >> uvw_v_offset;
			//ifile.seekg(pos);
			ulFlags |= MAPVOFFSET;
		}

		if (FindInFileBefore(ifile, szUVW_U_TILING, szSTOP))
		{
			ifile >> uvw_u_tiling;
			//ifile.seekg(pos);
			ulFlags |= MAPUTILING;
		}

		if (FindInFileBefore(ifile, szUVW_V_TILING, szSTOP))
		{
			ifile >> uvw_v_tiling;
			//ifile.seekg(pos);
			ulFlags |= MAPVTILING;
		}

		if (FindInFileBefore(ifile, szUVW_ANGLE, szSTOP))
		{
			ifile >> uvw_angle;
			//ifile.seekg(pos);
			ulFlags |= MAPANGLE;
		}

		if (FindInFileBefore(ifile, szUVW_BLUR, szSTOP))
		{
			ifile >> uvw_blur;
			//ifile.seekg(pos);
			ulFlags |= MAPBLUR;
		}

		if (FindInFileBefore(ifile, szUVW_BLUR_OFFSET, szSTOP))
		{
			ifile >> uvw_blur_offset;
			//ifile.seekg(pos);
			ulFlags |= MAPBLUROFFSET;
		}

		if (FindInFileBefore(ifile, szUVW_NOISE_AMT, szSTOP))
		{
			ifile >> uvw_noise_amt;
			//ifile.seekg(pos);
			ulFlags |= MAPNOISEAMT;
		}

		if (FindInFileBefore(ifile, szUVW_NOISE_SIZE, szSTOP))
		{
			ifile >> uvw_noise_size;
			//ifile.seekg(pos);
			ulFlags |= MAPNOISESIZE;
		}

		if (FindInFileBefore(ifile, szUVW_NOISE_LEVEL, szSTOP))
		{
			ifile >> uvw_noise_level;
			//ifile.seekg(pos);
			ulFlags |= MAPNOISELEVLE;
		}

		if (FindInFileBefore(ifile, szUVW_NOISE_PHASE, szSTOP))
		{
			ifile >> uvw_noise_phase;
			//ifile.seekg(pos);
			ulFlags |= MAPNOISEPHASE;
		}

		if (FindInFileBefore(ifile, szBITMAP_FILTER, szSTOP))
		{
			ifile >> bitmap_filter;
			//ifile.seekg(pos);
			ulFlags |= MAPBITMAPFILTER;
		}

	}
	catch(ERRORCODE e){
		iErrorNo = e;
		return false;
	}    
	catch(int i){
		iErrorNo = i;
		return false;
	}
	catch(...){
		iErrorNo = UNKNOWNERROR;
		return false;
	}
	return true;
}



ASE::ASEFile::ASEFile():iWarning(0),scenename("NO_NAME")
{
	background_static[0] = background_static[1] = background_static[2] = 0.0f;
	ambient_static[0] = ambient_static[1] = ambient_static[2] = 0.0f;
}

//   Opens the file specified and passes it immediately to 
//   read(std::ifstream&).
int ASE::ASEFile::read(const char* szFileName)
{
	using namespace std;
	
//#ifdef MFC_ENABLED
//	std::string strFileName(szFileName + 2); //to eliminate the "./" prefix
//	strFileName = CDecoDirectoryManager::GetSingleton()->GetUserDataFolder() + "\\" + strFileName;
//	szFileName = strFileName.c_str();
//#endif
	ifstream ifile( szFileName );

	if( false == ifile.is_open() )
	{
		return 1;
	}

	char buf[MYBUFFER];
	::strcpy( buf, szFileName );
	// ExtractFile( buf );
	scenename = buf;

	size_t len = ::strlen(szFileName);
	while (szFileName[len] != '/' && szFileName[len] != '\\' && len)
		--len;
	path = len ? string(szFileName, szFileName + len + 1) : "";	

	return read(ifile);
}

//  Reads in an entire ASE file.
int ASE::ASEFile::read(std::ifstream& ifile)
{
	char buf[128];

	// Clear contents.
	background_static[0] = background_static[1] = background_static[2] = 0.0f;
	ambient_static[0] = ambient_static[1] = ambient_static[2] = 0.0f;
	materials.clear();
	geomobjects.clear();
	//scenename = "";
	iWarning = 0;

	try{
		// The file MUST have this header identifier
		ifile >> buf;
		assert( 0 == ::strcmp( buf, "*3DSMAX_ASCIIEXPORT" ) );
		if( ::strcmp( buf, "*3DSMAX_ASCIIEXPORT" ) )throw( INVALIDASEFILE );

		// Look for the scene header.
		// if( false == FindInFileBefore(ifile, szSCENE, szMATERIAL_LIST) )throw( INVALIDASEFILE );

		if( FindInFileBefore(ifile, szSCENE_BACKGROUND_STATIC, szMATERIAL_LIST) )/*throw( INVALIDASEFILE );*/
		{
			ifile >> background_static[0] >> background_static[1] >> background_static[2];
		}

		if( FindInFileBefore(ifile, szSCENE_AMBIENT_STATIC, szMATERIAL_LIST) )/*throw( INVALIDASEFILE );*/
		{
			ifile >> ambient_static[0] >> ambient_static[1] >> ambient_static[2];
		}

		// Check to see if the file has materials. It is permitted not to.
		// Find and read in material list.
		int imatcount = 0;
		if( FindInFile(ifile, szMATERIAL_LIST) && FindInFile(ifile, szMATERIAL_COUNT) )
		{
			ifile >> imatcount;
		}

		// Go through the materials (which we are told must be here)
		// and read them in.
		if( imatcount > 0 )
		{
			int iDummy = 0;
			for( int i=0; i < imatcount; ++i )
			{
				if( false == FindInFile(ifile, szMATERIAL) )throw( INVALIDASEFILE );
				ifile >> iDummy;
				assert( i == iDummy || !"Error mismatched material and dummy." );
				if( i != iDummy )throw( INVALIDASEFILE );

				ASE::Material m;

				// set path here
				m.path = path;
				// this is just for performance
				if ( i != imatcount - 1)
				{
					if( false == m.read( ifile, szMATERIAL ) )
						throw( m.GetError() );
				}
				else
				{
					if( false == m.read( ifile, szGEOMOBJECT ) )
						throw( m.GetError() );
				}
				materials.push_back( m );
			}
		}
		assert( imatcount == materials.size() );

		// Find and read in all Geomobjects. There is no list of these so we just have to look until
		// the end of file.
		while( FindInFile(ifile, szGEOMOBJECT) )
		{
			ASE::GeomObject gobject;
			if( false == gobject.read(ifile) )
			{
				switch( gobject.GetError() )
				{
				case NOFACES:               //  Objects with no faces are not considered 
					++iWarning;             //  to be fatal errors. These null objects
					break;                  //  are not added to the lists.
				case NOVERTICES:            //  Objects with no vertex are the same.
					++iWarning;
					break;
				default:
					throw(gobject.GetError());
				}
			}
			else                                //  Valid object found.
			{
				geomobjects.push_back( gobject );
			}
		}
	}
	catch(ERRORCODE e){
		ifile.close();
		return e;
	}
	catch(int i){
		ifile.close();
		return i;
	}
	catch(...){
		ifile.close();
		return -999;
	}
	ifile.close();
	return 0;
}

int ASE::ASEFile::GetFaceCount(void)const throw()
{
	int ret = 0;
	for (unsigned int i = 0; i < geomobjects.size(); ++i )
	{
		ret += geomobjects[i].mesh.face_count;
	}
	return ret;
}